package org.codingeasy.hotbean;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import org.springframework.stereotype.Component;

/**
 * 热类加载器管理
 *
 * @author : KangNing Hu
 */
@Component
public class HotClassLoaderManager {

  /**
   * class name -> 类加载器
   */
  private Map<String, HotClassLoader> classLoaderMap = new HashMap<>();

  /**
   * 读写锁
   */
  private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

  /**
   * 获取class loader
   * @param className class name
   * @return
   */
  public ClassLoader getClassLoader(String className){
    ReadLock readLock = readWriteLock.readLock();
    readLock.lock();
    try {
      return classLoaderMap.get(className);
    }finally {
      readLock.unlock();
    }
  }

  /**
   * 加载类
   * @param className 类名
   * @return
   */
  public Class<?> loadClass(String className) {
    return loadClass(className , null);
  }

  /**
   * 加载类
   *
   * @param className 类名
   * @param bytes 字节码
   * @return 返回加载的class
   */
  public Class<?> loadClass(String className, byte[] bytes) {
    /**
     * 读取classLoaderMap
     */
    ReadLock readLock = readWriteLock.readLock();
    readLock.lock();
    try {
      HotClassLoader hotClassLoader = classLoaderMap.get(className);
      if (hotClassLoader != null) {
        return hotClassLoader.loadClass(className);
      }
    } catch (Exception e) {
      throw new RuntimeException(e.getMessage(), e);
    } finally {
      readLock.unlock();
    }
    /**
     * 创建class loader 写入 读取classLoaderMap
     */
    HotClassLoader hotClassLoader = new HotClassLoader(bytes);
    WriteLock writeLock = readWriteLock.writeLock();
    writeLock.lock();
    try {
      classLoaderMap.put(className, hotClassLoader);
      return hotClassLoader.loadClass(className);
    } catch (Exception e) {
      throw new RuntimeException(e.getMessage(), e);
    } finally {
      writeLock.unlock();
    }
  }

  /**
   * 移除class
   * @param className class名称
   */
  public void remove(String className){
    WriteLock writeLock = readWriteLock.writeLock();
    writeLock.lock();
    try {
      classLoaderMap.remove(className);
    }finally {
      writeLock.unlock();
    }
  }

  /**
   * 重新加载
   * @param className 类名
   * @param bytes 字节码
   * @return
   */
  public Class<?> reloadClass(String className, byte[] bytes){
    remove(className);
    return loadClass(className , bytes);
  }

}
